#include <math.h>
#include <time.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <vector>
#include "MySQL/mysql.h"

#include "CSemaphore.h"
#include "CLuaManager.h"
#include "CDb.h"
#include "CVanaTime.h"
#include "CPacket.h"
#include "CCharSkillPacket.h"
#include "CEventPacket.h"
#include "CEventMorePacket.h"
#include "CGuildMenuPacket.h"
#include "CShopMenuPacket.h"
#include "CShopBuyMenuPacket.h"
#include "CSpecialMessagePacket.h"
#include "CPositionPacket.h"
#include "CUtil.h"
#include "CItemContainer.h"
#include "CItem.h"
#include "assert.h"
#include "CMobHandler.h"
#include "CRaiseTractorMenuPacket.h"
#include "CAbility.h"

//#include "../include/includes.h"

std::vector<struct regions> CLuaManager::regionList[256];
lua_State* CLuaManager::m_state;
CSemaphore CLuaManager::m_LuaState;
CLuaManager::CLuaManager(void)
{
}

CLuaManager::~CLuaManager(void)
{
	lua_close( m_state );
}


template<typename T>
struct RegType
{
	const char * name;
	int(*mfunc)(lua_State*,T*);
};

/************************************************************************/
/* SCRIPT FUNCTION TABLES   (Thanks to the Ascent crew for inspiration!)*/
/************************************************************************/
RegType<CChar> PlayerMethods[] = {
	
	{ NULL, NULL },
};

RegType<CNpc> NpcMethods[] = {
	{ NULL, NULL },
};

RegType<CSpell> SpellMethods[] = {
	{ "getMPCost"		, &l_getMPCost},
	{ "getElement"		, &l_getSpellElement },
	{ "getCurrTargNum"	, &l_getCurrentSpellTargetNum},
	{ "getTargNum"		, &l_getSpellTargetNum},
	{ "setMsg"			, &l_setMsg},
    { "setAnimation"    , &l_setSpellAnimation},
	{ NULL, NULL },
};

RegType<CAbility> AbilityMethods[] = {
	{ "setMsg"			, &l_setAbilityMessage},
	{ "setResult"		, &l_setAbilityResult},
	{ "setAnimation"    , &l_setAbilityAnimation},
	{ NULL, NULL },
};

RegType<CItem> ItemMethods[] = {
	{ "setMsg"			, &l_setItemMessage},
	{ "setAnimation"	, &l_setItemAnimation},
	{ "dontRemove"		, &l_setItemRemove},
	{ NULL, NULL },
};

RegType<CShopBuyMenuPacket> ShopMethods[] = {
	{ "addItem",	&l_shopAddItem},
	{ NULL, NULL },
};

RegType<CEventPacket> EventMethods[] = {
	{ "setCallbackFile", &l_setEventCallbackFile},
	{ "setCallbackFunc", &l_setEventCallbackFunc},
	{ "setParams",		 &l_setEventParams},
	{ NULL, NULL },
};


RegType<CBaseEntity> BaseEntityMethods[] = {
    // SHARED FUNCTIONS
	{ "getName",	&l_getCharName },
	{ "getId",		&l_getCharId },
	{ "getMainJob", &l_getCharJob },
	{ "getMainLvl", &l_getCharLvl },
	{ "getSubJob",  &l_getCharSubJob },
	{ "getSubLvl",  &l_getCharSubLvl },
	{ "getHP",		&l_getCharHp },
	{ "getMP",		&l_getCharMp },
	{ "getTP",		&l_getTp },
	{ "getStat",    &l_getStat },

	{ "getAtt",     &l_getAtt },
	{ "getAcc",     &l_getAcc },
	{ "getDef",     &l_getDef },
	{ "getEva",     &l_getEva },

	{ "getBaseDmg", &l_getBaseDamage },

	{ "getTarget",	&l_getTarget },

	{ "getElementalResist",	&l_getElementalResist },

	{ "setHP",		&l_setHp },
	{ "setMP",		&l_setMp },
	{ "setTP",		&l_setTp },
	{ "getMaxHP",	&l_getMaxHp },
	{ "getMaxMP",	&l_getMaxMp },
	{ "addHP",		&l_addHp },
	{ "addMP",		&l_addMp },
	{ "removeHP",	&l_removeHp },
	{ "removeMP",	&l_removeMp },

	{ "getXPos",	&l_getCharXPos },
	{ "getYPos",	&l_getCharYPos },
	{ "getZPos",	&l_getCharZPos },
	{ "getRot",		&l_getCharRotation },
	{ "getZone",	&l_getCharZone },

	{ "isPlayer",	&l_isPlayer },
	{ "isNPC",		&l_isNPC },
	{ "isMob",		&l_isMob },
	{ "updateEnmity",&l_updateEnmity },	
	{ "getVModifier",&l_getVModifier },
	{ "getCModifier",&l_getCModifier },


	//PLAYER ONLY
	{ "getRace",	&l_getCharRace },			
	{ "getNation",	&l_getCharNation },			
	{ "getVar",		&l_getCharVar },			
	{ "setVar",		&l_setCharVar },			
	{ "startEvent", &l_startEvent },			
	{ "updateEvent",&l_sendMoreEvent },			
	{ "addAndEquip",&l_CharAddAndEquipItem },	
	{ "addItem",	&l_CharAddItem },			
	{ "sendRelease",&l_sendRelease },			
	{ "showText",	&l_showNPCTextLine },		
	{ "getPrevZone",&l_getCharPrevZone },		
	{ "getCurrGil",	&l_getCharCurrGil },		
	{ "setCurrGil",	&l_setCharCurrGil },		
	{ "addGil",		&l_gainGil },				
	{ "removeGil",	&l_removeGil },
	{ "getItemInfo",&l_CharGetItemInfo },	
	{ "hasItem",	&l_CharHasItem },	
	{ "removeItem",	&l_CharTakeItem },	
	{ "addKeyItem",	&l_addKeyItem },	
	{ "hasKeyItem",	&l_hasKeyItem },	
	{ "seenKeyItem",&l_seenKeyItem },	
	{ "removeKeyItem",&l_removeKeyItem },
	{ "setPos",		&l_setCharPos },	
	{ "getFame",	&l_getFame },
	{ "getFameLevel",&l_getFameLevel },
	{ "setFame",	&l_setFame },
	{ "addFame",	&l_addFame },
	{ "setAnimation",&l_setCharAnimation },	
	{ "setNation",	&l_setCharNation },	
	{ "sendMenu",	&l_sendMenu },
	{ "createShop", &l_createShop },
	{ "sendShop",	&l_sendShop },
	{ "sendGuild",  &l_sendGuild },
	{ "getSkill",	&l_getSkill },
	{ "getSkillRank",&l_getSkillRank },
	{ "setSkillRank",&l_setSkillRank },
	{ "getEquip",	&l_getEquip },
	{ "specialMessage",&l_sendSpecialMessage },
	{ "addQuest",	&l_addQuest },
	{ "completeQuest",&l_completeQuest },
	{ "getQuestStatus",&l_getQuestStatus },
	{ "currentMission",&l_currentMission },
	{ "completeMission",&l_completeMission },
	{ "getMissionStatus",&l_getMissionStatus },
	{ "getRank",	&l_getCharRank },			
	{ "rankUp",		&l_charRankUp },	
	{ "getRankPoints",&l_getCharRankPoints },			
	{ "addRankPoints",&l_addCharRankPoints },
	{ "getLevelCap",&l_getLevelCap },
	{ "setLevelCap",&l_setLevelCap },
	{ "increaseStorage",&l_increaseStorage },
	{ "getTitle",	&l_getTitle },
	{ "setTitle",	&l_setTitle },
	{ "spawnPet",	&l_spawnPet },
	{ "unlockSubjobs",&l_charUnlockSubjobs },
	{ "unlockJob",	&l_charUnlockJob },
	{ "getFreeSlotCount", &l_getFreeSlotCount },
	{ "learnWs", &l_learnWs },
	{ "addSpell", &l_addSpell },
	{ "hasSpell", &l_hasSpell },
	{ "sendTractorMenu", &l_sendTractor },
	{ "sendRaiseMenu", &l_sendRaise },
	{ "getWeaponDelay", &l_getWeaponDelay },
	{ "setHomePoint", &l_setCharHomePoint },
	{ "warp", &l_warpChar },
	
	//NPC ONLY
	{ "getState",   &l_getNPCState },			
	{ "setState",   &l_setNPCState },	

	{ "getFamily",  &l_getFamilyId },	

	
	{ NULL, NULL },
};

RegType<CTradeContainer> TradeContainerMethods[] = {
	{ "getGil",      &l_getTradeGil},
	{ "getItem",	 &l_getItemID},
	{ "getItemQty",  &l_getItemQuantity},
	{ "getSlotQty",  &l_getSlotQuantity},
	{ "getItemCount",&l_getItemCount},
	{ "hasItemQty",  &l_hasItemQuantity},
	{ "isComplete",  &l_tradeComplete},
	{ NULL, NULL },
};

RegType<CZone> ZoneMethods[] = {
	{ "getWeather", &l_getWeather},
	{ "spawnMob", &l_spawnMob},
	{ NULL, NULL },
};



template<typename T> RegType<T>* GetMethodTable() { return NULL; }
template<>
RegType<CChar>* GetMethodTable<CChar>() { return PlayerMethods; }

template<>
RegType<CSpell>* GetMethodTable<CSpell>() { return SpellMethods; }

template<>
RegType<CTradeContainer>* GetMethodTable<CTradeContainer>() { return TradeContainerMethods; }

template<>
RegType<CShopBuyMenuPacket>* GetMethodTable<CShopBuyMenuPacket>() { return ShopMethods; }

template<>
RegType<CEventPacket>* GetMethodTable<CEventPacket>() { return EventMethods; }

template<>
RegType<CNpc>* GetMethodTable<CNpc>() { return NpcMethods; }

template<>
RegType<CBaseEntity>* GetMethodTable<CBaseEntity>() { return BaseEntityMethods; }

template<>
RegType<CZone>* GetMethodTable<CZone>() { return ZoneMethods; }

template<>
RegType<CAbility>* GetMethodTable<CAbility>() { return AbilityMethods; }

template<>
RegType<CItem>* GetMethodTable<CItem>() { return ItemMethods; }

template<typename T> const char * GetTClassName() { return "UNKNOWN"; }
template<>
const char * GetTClassName<CChar>()
{
	return "CChar";
}
template<>
const char * GetTClassName<CNpc>()
{
	return "CNpc";
}
template<>
const char * GetTClassName<CSpell>()
{
	return "CSpell";
}
template<>
const char * GetTClassName<CBaseEntity>()
{
	return "CBaseEntity";
}
template<>
const char * GetTClassName<CShopBuyMenuPacket>()
{
	return "CShopBuyMenuPacket";
}
template<>
const char * GetTClassName<CEventPacket>()
{
	return "CEventPacket";
}
template<>
const char * GetTClassName<CTradeContainer>()
{
	return "CTradeContainer";
}
template<>
const char * GetTClassName<CZone>()
{
	return "CZone";
}
template<>
const char * GetTClassName<CAbility>()
{
	return "CAbility";
}
template<>
const char * GetTClassName<CItem>()
{
	return "CItem";
}


template <typename T> class Lunar {
	typedef struct { T *pT; } userdataType;
public:
	typedef int (*mfp)(lua_State *L, T* ptr);
	typedef struct { const char *name; mfp mfunc; } RegType;

	static void Register(lua_State *L) {
		lua_newtable(L);
		int methods = lua_gettop(L);

		luaL_newmetatable(L, GetTClassName<T>());
		int metatable = lua_gettop(L);

		// store method table in globals so that
		// scripts can add functions written in Lua.
		lua_pushvalue(L, methods);
		set(L, LUA_GLOBALSINDEX, GetTClassName<T>());

		// hide metatable from Lua getmetatable()
		lua_pushvalue(L, methods);
		set(L, metatable, "__metatable");

		lua_pushvalue(L, methods);
		set(L, metatable, "__index");

		lua_pushcfunction(L, tostring_T);
		set(L, metatable, "__tostring");

		lua_pushcfunction(L, gc_T);
		set(L, metatable, "__gc");

		lua_newtable(L);                // mt for method table
		lua_pushcfunction(L, new_T);
		lua_pushvalue(L, -1);           // dup new_T function
		set(L, methods, "new");         // add new_T to method table
		set(L, -3, "__call");           // mt.__call = new_T
		lua_setmetatable(L, methods);

		// fill method table with methods from class T
		for (RegType *l = ((RegType*)GetMethodTable<T>()); l->name; l++) {
			lua_pushstring(L, l->name);
			lua_pushlightuserdata(L, (void*)l);
			lua_pushcclosure(L, thunk, 1);
			lua_settable(L, methods);
		}

		lua_pop(L, 2);  // drop metatable and method table
	}

	// call named lua method from userdata method table
	static int call(lua_State *L, const char *method,
		int nargs=0, int nresults=LUA_MULTRET, int errfunc=0)
	{
		int base = lua_gettop(L) - nargs;  // userdata index
		if (!luaL_checkudata(L, base,  GetTClassName<T>())) {
			lua_settop(L, base-1);           // drop userdata and args
			lua_pushfstring(L, "not a valid %s userdata",  GetTClassName<T>());
			return -1;
		}

		lua_pushstring(L, method);         // method name
		lua_gettable(L, base);             // get method from userdata
		if (lua_isnil(L, -1)) {            // no method?
			lua_settop(L, base-1);           // drop userdata and args
			lua_pushfstring(L, "%s missing method '%s'",  GetTClassName<T>(), method);
			return -1;
		}
		lua_insert(L, base);               // put method under userdata, args

		int status = lua_pcall(L, 1+nargs, nresults, errfunc);  // call method
		if (status) {
			const char *msg = lua_tostring(L, -1);
			if (msg == NULL) msg = "(error with no message)";
			lua_pushfstring(L, "%s:%s status = %d\n%s",
				 GetTClassName<T>(), method, status, msg);
			lua_remove(L, base);             // remove old message
			return -1;
		}
		return lua_gettop(L) - base + 1;   // number of results
	}

	// push onto the Lua stack a userdata containing a pointer to T object
	static int push(lua_State *L, T *obj, bool gc=false) {
		if (!obj) { lua_pushnil(L); return 0; }
		luaL_getmetatable(L, GetTClassName<T>());  // lookup metatable in Lua registry
		if (lua_isnil(L, -1)) luaL_error(L, "%s missing metatable", GetTClassName<T>());
		int mt = lua_gettop(L);
		subtable(L, mt, "userdata", "v");
		userdataType *ud =
			static_cast<userdataType*>(pushuserdata(L, obj, sizeof(userdataType)));
		if (ud) {
			ud->pT = obj;  // store pointer to object in userdata
			lua_pushvalue(L, mt);
			lua_setmetatable(L, -2);
			if (gc == false) {
				lua_checkstack(L, 3);
				subtable(L, mt, "do not trash", "k");
				lua_pushvalue(L, -2);
				lua_pushboolean(L, 1);
				lua_settable(L, -3);
				lua_pop(L, 1);
			}
		}
		lua_replace(L, mt);
		lua_settop(L, mt);
		return mt;  // index of userdata containing pointer to T object
	}

	// get userdata from Lua stack and return pointer to T object
	static T *check(lua_State *L, int narg) {
		userdataType *ud =
			static_cast<userdataType*>(luaL_checkudata(L, narg, GetTClassName<T>()));
		if(!ud) { luaL_typerror(L, narg, GetTClassName<T>()); return NULL; }
		return ud->pT;  // pointer to T object
	}

private:
	Lunar();  // hide default constructor

	// member function dispatcher
	static int thunk(lua_State *L) {
		// stack has userdata, followed by method args
		T *obj = check(L, 1);  // get 'self', or if you prefer, 'this'
		lua_remove(L, 1);  // remove self so member function args start at index 1
		// get member function from upvalue
		RegType *l = static_cast<RegType*>(lua_touserdata(L, lua_upvalueindex(1)));
		//return (obj->*(l->mfunc))(L);  // call member function
		return l->mfunc(L,obj);
	}

	// create a new T object and
	// push onto the Lua stack a userdata containing a pointer to T object
	static int new_T(lua_State *L) {
		lua_remove(L, 1);   // use classname:new(), instead of classname.new()
		T *obj = NULL/*new T(L)*/;  // call constructor for T objects
		assert(false);
		push(L, obj, true); // gc_T will delete this object
		return 1;           // userdata containing pointer to T object
	}

	// garbage collection metamethod
	static int gc_T(lua_State *L) {
		if (luaL_getmetafield(L, 1, "do not trash")) {
			lua_pushvalue(L, 1);  // dup userdata
			lua_gettable(L, -2);
			if (!lua_isnil(L, -1)) return 0;  // do not delete object
		}
		userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
		T *obj = ud->pT;
		if (obj) delete obj;  // call destructor for T objects
		return 0;
	}

	static int tostring_T (lua_State *L) {
		char buff[32];
		userdataType *ud = static_cast<userdataType*>(lua_touserdata(L, 1));
		T *obj = ud->pT;
		sprintf(buff, "%p", obj);
		lua_pushfstring(L, "%s (%s)", GetTClassName<T>(), buff);
		return 1;
	}

	static void set(lua_State *L, int table_index, const char *key) {
		lua_pushstring(L, key);
		lua_insert(L, -2);  // swap value and key
		lua_settable(L, table_index);
	}

	static void weaktable(lua_State *L, const char *mode) {
		lua_newtable(L);
		lua_pushvalue(L, -1);  // table is its own metatable
		lua_setmetatable(L, -2);
		lua_pushliteral(L, "__mode");
		lua_pushstring(L, mode);
		lua_settable(L, -3);   // metatable.__mode = mode
	}

	static void subtable(lua_State *L, int tindex, const char *name, const char *mode) {
		lua_pushstring(L, name);
		lua_gettable(L, tindex);
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);
			lua_checkstack(L, 3);
			weaktable(L, mode);
			lua_pushstring(L, name);
			lua_pushvalue(L, -2);
			lua_settable(L, tindex);
		}
	}

	static void *pushuserdata(lua_State *L, void *key, size_t sz) {
		void *ud = 0;
		lua_pushlightuserdata(L, key);
		lua_gettable(L, -2);     // lookup[key]
		if (lua_isnil(L, -1)) {
			lua_pop(L, 1);         // drop nil
			lua_checkstack(L, 3);
			ud = lua_newuserdata(L, sz);  // create new userdata
			lua_pushlightuserdata(L, key);
			lua_pushvalue(L, -2);  // dup userdata
			lua_settable(L, -4);   // lookup[key] = userdata
		}
		return ud;
	}
};

void report(lua_State * L)
{
	const char * msg= lua_tostring(L,-1);
	while(msg)
	{
		lua_pop(L,-1);
		printf("\t%s\n", msg);
		msg=lua_tostring(L,-1);
	}
}

#define LUNAR_DECLARE_METHOD(Class, Name) {#Name, &Class::Name}

void CLuaManager::OnServerStart() {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/globals/global.lua");

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return ;  
	}


	lua_pushstring(m_state, "OnServerStart");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onServerStart from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return ;
	}
	
	int r = lua_pcall(m_state,0,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

void CLuaManager::OnCharCreate(CChar * schar) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/globals/Server.lua");

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return ;  
	}


	lua_pushstring(m_state, "onCharCreate");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onCharCreate from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return ;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	
	int r = lua_pcall(m_state,1,LUA_MULTRET,0);
	if(r)
		report(m_state);

	CLuaManager::m_LuaState.unlock();
	
}


void CLuaManager::OnZoneInitialize(int zoneID) {

	char file[255];
	memset(file,0,255);
	
	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/zones/%s/Zone.lua", CUtil::getZoneName(zoneID));

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		//CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	lua_pushstring(m_state, "onInitialize");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onInitialize from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return ;
	}
	
	int r = lua_pcall(m_state,0,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();
}

int CLuaManager::OnZoneIn(CChar * schar, int prevzone) {

	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/zones/%s/Zone.lua", CUtil::getZoneName(schar->getZone()));

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return -1;  
	}

	memset(schar->eventQ.eventScript, 0, 255);
	memcpy(schar->eventQ.eventScript, file, strlen(file));

	lua_pushstring(m_state, "onZoneIn");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onZoneIn from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return -1;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	lua_pushinteger(m_state,prevzone);
	
	int r = lua_pcall(m_state,2,LUA_MULTRET,0);
	if(r)
		report(m_state);

	CLuaManager::m_LuaState.unlock();
	return (int)lua_tonumber(m_state,-1);

}

int CLuaManager::OnUseWeaponSkill(CChar * schar, CBaseEntity * target, int wsID) {

	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/globals/weaponskills/%s.lua", CUtil::getWeaponSkillName(wsID));

	for(unsigned int i = 0; i < strlen(file); i++) {
		if(file[i] == ':') {
			file[i] = '_';
		} else if(file[i] == ' ') {
			file[i] = '_';
		} else if(file[i] == '\'') {
			file[i] = '_';
		}
	}

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return 0;  
	}


	lua_pushstring(m_state, "onUseWeaponSkill");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onUseWeaponSkill from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return 0;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	if(target!=NULL)
		Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)target);
	else
		lua_pushnil(m_state);

	lua_pushinteger(m_state,wsID);
	
	int r = lua_pcall(m_state,3,LUA_MULTRET,0);
	if(r)
		report(m_state);

	CLuaManager::m_LuaState.unlock();
	return (int)lua_tonumber(m_state,-1);

}

void CLuaManager::OnRegionEnter(CChar * schar, int regionID) {

	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/zones/%s/Zone.lua", CUtil::getZoneName(schar->getZone()));

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return ;  
	}

	lua_pushstring(m_state, "onRegionEnter");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onRegionEnter from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return ;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	lua_pushinteger(m_state,regionID);
	
	int r = lua_pcall(m_state,2,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

int CLuaManager::OnUseAbility(CChar * schar, CBaseEntity * target, CAbility * pAbility, bool isFirstTarget) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/globals/abilities/%s.lua", pAbility->getAbilityName());

	for(unsigned int i = 0; i < strlen(file); i++) {
		if(file[i] == ':') {
			file[i] = '_';
		} else if(file[i] == ' ') {
			file[i] = '_';
		} else if(file[i] == '\'') {
			file[i] = '_';
		}
	}

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return 0;  
	}


	lua_pushstring(m_state, "OnUseAbility");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function OnUseAbility from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return 0;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	if(target!=NULL)
		Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)target);
	else
		lua_pushnil(m_state);

	Lunar<CAbility>::push(m_state, pAbility);

	lua_pushinteger(m_state, isFirstTarget);


	
	
	int r = lua_pcall(m_state,4,LUA_MULTRET,0);
	if(r)
		report(m_state);

	CLuaManager::m_LuaState.unlock();
	return 0;

}

int CLuaManager::OnUseItem(CChar * schar, CBaseEntity * target, CItem * pItem) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, "scripts/globals/items/%s.lua", pItem->getItemName());

	for(unsigned int i = 0; i < strlen(file); i++) {
		if(file[i] == ':') {
			file[i] = '_';
		} else if(file[i] == ' ') {
			file[i] = '_';
		} else if(file[i] == '\'') {
			file[i] = '_';
		}
	}

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1));  
		CLuaManager::m_LuaState.unlock();
		return 0;  
	}


	lua_pushstring(m_state, "OnUseItem");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function OnUseItem from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return 0;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	if(target!=NULL)
		Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)target);
	else
		lua_pushnil(m_state);

	Lunar<CItem>::push(m_state, pItem);	
	
	int r = lua_pcall(m_state,3,LUA_MULTRET,0);
	if(r)
		report(m_state);

	CLuaManager::m_LuaState.unlock();
	return 0;
}

void CLuaManager::OnTrigger(CChar * schar, CBaseEntity * npc) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();
	char * name[17];
	memset(name, 0 , 17);
	memcpy(name, npc->getStrippedName(), 16);
	name[16] = '\0';
	
	sprintf(file, "scripts/zones/%s/npcs/%s.lua", CUtil::getZoneName( schar->getZone() ), name);


	if(((CNpc*)npc)->type == 0x07) {
		if(!CLuaManager::scriptExists(file)) {
			strcpy(file, "scripts/globals/trigger.lua");
		} else {
			memset(schar->eventQ.eventScript, 0, 255);
			memcpy(schar->eventQ.eventScript, file, strlen(file));
		}
	} else {
		memset(schar->eventQ.eventScript, 0, 255);
		memcpy(schar->eventQ.eventScript, file, strlen(file));
	}

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	lua_pushstring(m_state, "onTrigger");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onTrigger from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	if(npc!=NULL)
		Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)npc);
	else
		lua_pushnil(m_state);
	
	int r = lua_pcall(m_state,2,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

void CLuaManager::OnTrade(CChar * schar, CBaseEntity * npc, CTradeContainer * tradeItems) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();
	
	sprintf(file, "scripts/zones/%s/npcs/%s.lua", CUtil::getZoneName(schar->getZone()), npc->getStrippedName());

	memset(schar->eventQ.eventScript, 0, 255);
	memcpy(schar->eventQ.eventScript, file, strlen(file));

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	lua_pushstring(m_state, "onTrade");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function onTrade from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	if(npc!=NULL)
		Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)npc);
	else
		lua_pushnil(m_state);

	Lunar<CTradeContainer>::push(m_state, tradeItems);
	
	
	int r = lua_pcall(m_state, 3 ,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}


void CLuaManager::OnEventFinish(CChar * schar, int eventID, int result)
{
	char file[255];
	memset(file,0,255);
	char function[255];
	memset(function,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, schar->eventQ.eventScript);

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	if(!strcmp(schar->eventQ.eventScriptFunction, "none")) {
		sprintf(function, "onEventFinish");
	} else {
		sprintf(function, schar->eventQ.eventScriptFunction);
	}
	lua_pushstring(m_state, function);
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function %s from Project Axium!\n", function);
		CLuaManager::m_LuaState.unlock();
		return;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	lua_pushinteger(m_state,eventID);
	lua_pushinteger(m_state,result);
	
	int r = lua_pcall(m_state,3,LUA_MULTRET,0);
	schar->eventQ.reset();
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

void CLuaManager::OnEventSelection(CChar * schar, int eventID, int result)
{
	char file[255];
	memset(file,0,255);
	char function[255];
	memset(function,0,255);

	CLuaManager::m_LuaState.tryLock();

	sprintf(file, schar->eventQ.eventScript);

	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	if(!strcmp(schar->eventQ.eventScriptFunction, "none")) {
		sprintf(function, "onEventSelection");
	} else {
		sprintf(function, schar->eventQ.eventScriptFunction);
	}
	lua_pushstring(m_state, function);
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function %s from Project Axium!\n", function);
		CLuaManager::m_LuaState.unlock();
		return;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	lua_pushinteger(m_state,eventID);
	lua_pushinteger(m_state,result);
	
	int r = lua_pcall(m_state,3,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

int CLuaManager::OnSpellCast(CChar * schar, CSpell * spell) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.lock();
	
	sprintf(file, "scripts/globals/spells/%s.lua",spell->getSpellName());

	for(unsigned int i = 0; i < strlen(file); i++) {
		if(file[i] == ':') {
			file[i] = '_';
		}
	}


	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return 0;  
	}

	lua_pushstring(m_state, "OnSpellCast");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function OnSpellCastFinish from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return 0;
	}
	
	CBaseEntity * tmp = CChar::getCChar(spell->getSpellTargetID(),"",0);

	if(tmp == NULL) {
		tmp = CZoneHandler::getInstance()->getNpc(spell->getSpellTargetID(),schar->getZone());
	}
	if(tmp == NULL) {
		tmp = CZoneHandler::getInstance()->getMob(spell->getSpellTargetID(),schar->getZone());
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)tmp);
	Lunar<CSpell>::push(m_state, spell);
	
	
	int r = lua_pcall(m_state,3,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();
	return (int)lua_tonumber(m_state,-1);

}

void CLuaManager::OnSpellCastFinish(CChar * schar, CSpell * spell) {
	char file[255];
	memset(file,0,255);

	CLuaManager::m_LuaState.tryLock();
	
	sprintf(file, "scripts/globals/spellcast.lua");


	if (luaL_loadfile(m_state, file) || lua_pcall(m_state, 0, 0, 0)) {  
		CConsole::outErr("Lua error: %s", lua_tostring(m_state, -1)); 
		CLuaManager::m_LuaState.unlock();
		return;  
	}

	lua_pushstring(m_state, "OnSpellCastFinish");
	lua_gettable(m_state, LUA_GLOBALSINDEX);
	if(lua_isnil(m_state,-1))
	{
		CConsole::outErr("Tried to call invalid LUA function OnSpellCastFinish from Project Axium!\n");
		CLuaManager::m_LuaState.unlock();
		return;
	}

	Lunar<CBaseEntity>::push(m_state, (CBaseEntity*)schar);
	
	Lunar<CSpell>::push(m_state, spell);
	
	
	int r = lua_pcall(m_state,2,LUA_MULTRET,0);
	if(r)
		report(m_state);
	CLuaManager::m_LuaState.unlock();

}

int CLuaManager::Initialize(){
	m_state = lua_open();
	luaL_openlibs(m_state);

	lua_register( m_state, "setNPCAnimation",		l_setNPCAnimation);
	lua_register( m_state, "setCharZone",			l_setCharZone);
	
	//GLOBAL LUA FUNCS
	lua_register( m_state, "executeSQL",			l_executeSQL);
	lua_register( m_state, "Event",					l_createEvent);
	lua_register( m_state, "Zone",					l_getZone);
	lua_register( m_state, "registerRegion",		l_registerRegion);

	lua_register( m_state, "getVanaYear",			l_getVanaYear);
	lua_register( m_state, "getVanaMonth",			l_getVanaMonth);
	lua_register( m_state, "getVanaDay",			l_getVanaDay);
	lua_register( m_state, "getVanaDayElement",		l_getVanaDayElement);
	lua_register( m_state, "getVanaHour",			l_getVanaHour);
	lua_register( m_state, "getVanaMinute",			l_getVanaMinute);

	lua_register( m_state, "setGlobalVar",			l_setGlobalVar);
	lua_register( m_state, "getGlobalVar",			l_getGlobalVar);

	lua_register( m_state, "getNPCById",			l_getNPCById );
	lua_register( m_state, "getNPCByTargId",		l_getNPCByTargId);
	lua_register( m_state, "sendTellMsg",			l_sendTellMsg);



	Lunar<CBaseEntity>::Register(m_state);
	Lunar<CNpc>::Register(m_state);
	Lunar<CShopBuyMenuPacket>::Register(m_state);
	Lunar<CEventPacket>::Register(m_state);
	Lunar<CTradeContainer>::Register(m_state);
	Lunar<CSpell>::Register(m_state);
	Lunar<CZone>::Register(m_state);
	Lunar<CAbility>::Register(m_state);
	Lunar<CItem>::Register(m_state);

	return 1;
}

bool CLuaManager::scriptExists(char * filename){
	FILE *fp;
	fp = fopen(filename, "r");
	if(fp) {
		fclose(fp);
		return true;
	}
	else {
		return false;
	}
}

inline LUA_API lua_Number lua_popnumber(lua_State *L)
{
  register lua_Number tmp = lua_tonumber(L, lua_gettop(L));
  lua_pop(L, 1);
  return tmp;
}

inline LUA_API const char *lua_popstring(lua_State *L)
{
  register const char *tmp = lua_tostring(L, lua_gettop(L));
  lua_pop(L, 1);
  return tmp;
}

///////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////
/// SCRIPT FUNCTION DEFINITIONS
///////////////////////////////////////////////////////////////////////////
/// GET FUNCTIONS FOR NPCS


int l_getNPCState( lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getAnimation() );
	return 1;
}
int l_getNPCById( lua_State* luaVM) {
	Lunar<CBaseEntity>::push( luaVM, (CBaseEntity*)CZoneHandler::getInstance()->getNpc((int)lua_tointeger(luaVM, -2),(unsigned char)lua_tointeger(luaVM, -1)));
	return 1;
}
int l_getNPCByTargId( lua_State* luaVM) {
	Lunar<CBaseEntity>::push( luaVM, (CBaseEntity*)CZoneHandler::getInstance()->getNpcByTarg((int)lua_tointeger(luaVM, -2),(unsigned char)lua_tointeger(luaVM, -1)));
	return 1;
}

int l_getFamilyId( lua_State* luaVM, CBaseEntity * pEntity) {
	if (pEntity == NULL){
		return 0;
	}
	if (!pEntity->isNPC()) {
		return 0;
	}
	lua_pushinteger( luaVM,(lua_Integer)((CNpc*)pEntity)->m_familyId);
	return 1;
}

//this doesn't really set the state, it changes animation 9 to 8 and queues the change back 
int l_setNPCState( lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isNPC()){
		return 0;
	}
	int ttw = (int)lua_tointeger(luaVM, -1); 

	((CNpc*)pEntity)->openDoor();

	struct timer doorTimer;
	doorTimer.ptr = (LPVOID *)pEntity;
	doorTimer.timestamp = time(NULL);
	doorTimer.timetowait = ttw;
	doorTimer.type = 0x01;

	CZoneHandler::getInstance()->npcTimerList[pEntity->getZone()].push_back(doorTimer);

	return 1;
}

int l_updateEnmity(lua_State* luaVM, CBaseEntity * pEntity ) {

	if((pEntity == NULL)){
		return 0;
	}

	CBaseEntity * pSource = Lunar<CBaseEntity>::check(luaVM, 1);
	if((pSource == NULL)){
		return 0;
	}
	if(pEntity->getObjectType() == OBJ_MOB) {
		int vEnmity = (int)lua_tointeger(luaVM, -2);
		int cEnmity = (int)lua_tointeger(luaVM, -1);
		((CNpc*)pEntity)->takePhysicalDamage(0,pSource);
		((CNpc*)pEntity)->updateEnmity(pSource, vEnmity,cEnmity);
	}
	return 1;

}

int l_getVModifier(lua_State* luaVM, CBaseEntity * pEntity ) {

	if((pEntity == NULL)){
		return 0;
	}

	float enmityVModifier = pEntity->getVModifier();
	lua_pushinteger(luaVM,(lua_Integer)enmityVModifier);

	return 1;
}

int l_getCModifier(lua_State* luaVM, CBaseEntity * pEntity ) {

	if((pEntity == NULL)){
		return 0;
	}

	float enmityCModifier = pEntity->getCModifier();
	lua_pushinteger(luaVM,(lua_Integer)enmityCModifier);

	return 1;
}


int l_setNPCAnimation( lua_State* luaVM) {
	CNpc * pNpc = (CNpc *) lua_tointeger(luaVM, -3);
	CChar * schar = (CChar *) lua_tointeger(luaVM, -2);
	char animation = (char)lua_tointeger(luaVM, -1);

	pNpc->setAnimation((unsigned char)animation);
	CComm * comm = new CComm;
	comm->setComType(0x1234);
	memset(comm->message, 0, 200);
	comm->charid = pNpc->updateNpc((unsigned char *)comm->message);
	schar->insertComm(CHAR_INZONE_SELF, *comm, false);
	return 1;
}

int l_warpChar(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	if(((CChar*)pEntity)->st.homepoint != 0) {
		((CChar*)pEntity)->zoneChar(((CChar*)pEntity)->st.homepoint,((CChar*)pEntity)->m_homePoint);
	}
	return 1;
}

int l_showNPCTextLine( lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		return 0;
	}

	unsigned long messageID = (int)lua_tointeger(luaVM, -1); 

	CBaseEntity * pNpc = Lunar<CBaseEntity>::check(luaVM, 1);
	if((pNpc == NULL) || !pNpc->isNPC()){
		return 0;
	}

	((CChar*)pEntity)->sendNpcText(pNpc, messageID);
	return 1;
}

int l_sendMoreEvent( lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n <= 1) {
		CConsole::outTime("Could not process sendUpdate, Lack of arguments.");
		return 1;
	}
	
	if (n > 8) {
		CConsole::outTime("You've used %d arguments, sendUpdate only accepts 8.\nUnexpected results may occur.",n);
		n = 8;
	}

	CEventMorePacket * pEventMore = new CEventMorePacket();
	pEventMore->numOfParams = n;

	for (unsigned char i = 1; i <= (n); i++) {	
		pEventMore->m_param[n-i] = (unsigned int) lua_tonumber(luaVM,-i);
		CConsole::outTime("Argument #%i : %u", -i, pEventMore->m_param[n-i]);
	}

	((CChar*)pEntity)->inEvent = true;

	pEventMore->createPacket((CChar*)pEntity);

	return 1;

}

int l_sendRelease( lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		return 0;
	}
	int n = lua_gettop(luaVM);

	if (n > 1) {
		//If they used more than 1 parameter, they are mistaken for a different command
		CConsole::outTime("Could not process sendRelease, please check syntax.");	
	} else if (n == 1) {
		//If they use only 1 parameter, its a custom release type
		unsigned char releaseType = (unsigned char)lua_tointeger(luaVM, -1);
		((CChar*)pEntity)->release(releaseType);
	} else {
		//If there are no parameters it is a default release
		((CChar*)pEntity)->release();
	}

	return 1; 
}

int l_charUnlockSubjobs(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar*)pEntity)->unlockSubjobs();
	return 1;
}

int l_charUnlockJob(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 1) {
		CConsole::outTime("Not enough information for unlockJob[jobID]");
		return 0;
	}

	unsigned char jobID = (unsigned char)lua_tonumber(luaVM,-1);
	if( (jobID > 20) || (jobID == 0) )
		return 0;

	((CChar*)pEntity)->unlockJob(jobID);

	return 1;
}

int l_getFreeSlotCount(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n != 1) {
		CConsole::outTime("Not enough information for getFreeSlotCount[storage]");
		return 0;
	}

	unsigned char locationID = (unsigned char)lua_tonumber(luaVM,-1);
	if (locationID > 4)
		return 0;

	unsigned int skillValue =((CChar*)pEntity)->getFreeSlotCount(locationID);
	lua_pushinteger(luaVM,skillValue);

	return 1;
}

// Create a new shop packet and push the pointer to the script
int l_createShop(	lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 1) {
		CConsole::outTime("Not enough information for createShop[nation]");
		return 0;
	}

	unsigned char nation = (unsigned char)lua_tonumber(luaVM,-1);
	((CChar*)pEntity)->setShopNation(nation);
	unsigned char fameLevel = 0;
	fameLevel = ((CChar*)pEntity)->getFameLevel(nation);

	CShopBuyMenuPacket * pShop = new CShopBuyMenuPacket(fameLevel);
	Lunar<CShopBuyMenuPacket>::push(luaVM,pShop);
	return 1;
}

int l_shopAddItem( lua_State* luaVM, CShopBuyMenuPacket * pShopBuyMenu) {
	if(pShopBuyMenu == NULL) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information for addItem[itemID,price]");
		return 0;
	}
	unsigned short itemID = (unsigned short)lua_tonumber(luaVM,-2);
	unsigned long price = (unsigned long)lua_tonumber(luaVM,-1);

	pShopBuyMenu->addItem(itemID, price);

	CConsole::outDebOnly("Item: %i, Price: %i, Item number: %i",itemID,price,pShopBuyMenu->getNumItems());

	Lunar<CShopBuyMenuPacket>::push(luaVM,pShopBuyMenu);

	return 1;
}

int l_sendShop(	lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}

	CShopBuyMenuPacket * pShopBuyMenu  = Lunar<CShopBuyMenuPacket>::check(luaVM, 1);
	if(pShopBuyMenu == NULL)
		return 0;
	unsigned long numItems = pShopBuyMenu->getNumItems();
	CShopMenuPacket * pShopMenu = new CShopMenuPacket(numItems);

	pShopMenu->sendPacket((CChar*)pEntity);
	pShopBuyMenu->sendPacket((CChar*)pEntity);
	
	return 0;
}

int l_sendGuild( lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 4) {
		CConsole::outTime("Not enough information for sendGuild[guildID, holiday, open, close]");
		return 0;
	}
	unsigned short guildID = (unsigned short)lua_tonumber(luaVM,-4);
	unsigned char holiday = (unsigned char)lua_tonumber(luaVM,-3);
	unsigned char open = (unsigned char)lua_tonumber(luaVM,-2);
	unsigned char close = (unsigned char)lua_tonumber(luaVM,-1);
	unsigned char shop[2];

	memcpy(&shop, &guildID, 2);

	((CChar*)pEntity)->setShop(shop);
	((CChar*)pEntity)->quePacket(CGuildMenuPacket(holiday, open, close));
	
	return 0;
}

int l_getSkill(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 1) {
		CConsole::outTime("Not enough information for getSkill[skillID]");
		return 0;
	}

	unsigned short skillID = (unsigned short)lua_tonumber(luaVM,-1);
	unsigned short skillValue = ((CChar*)pEntity)->getSkill(skillID);
	skillValue = (skillValue/10);

	lua_pushinteger(luaVM,skillValue);

	return 1;
}

int l_getSkillRank(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 1) {
		CConsole::outTime("Not enough information for getSkillRank[skillID]");
		return 0;
	}

	unsigned short skillID = (unsigned short)lua_tonumber(luaVM,-1);
	unsigned char rank = ((CChar*)pEntity)->s.rank[skillID];

	lua_pushinteger(luaVM,rank);

	return 1;
}

int l_setSkillRank(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information for setSkillRank[skillID,rank]");
		return 0;
	}

	unsigned short skillID = (unsigned short)lua_tonumber(luaVM,-2);
	unsigned char rank = (unsigned char)lua_tonumber(luaVM,-1);
	((CChar*)pEntity)->s.rank[skillID] = rank;

	((CChar*)pEntity)->quePacket(CCharSkillPacket((CChar*)pEntity));

	return 1;
}

// Creates a new event and forwards the pointer to it to the script
int l_createEvent(		lua_State* luaVM) {
	unsigned int eventId = (unsigned int)lua_tointeger(luaVM, -1); 
	CEventPacket * ptrCep = new CEventPacket(eventId);

	Lunar<CEventPacket>::push(luaVM,ptrCep);
	return 1;
}

int l_setEventParams( lua_State* luaVM, CEventPacket * pEvent) {
	if(pEvent == NULL) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	int i;  //, param;
	if ((n < 1) || (n > 8)) {
		CConsole::outTime("Could not process setParams, Lack of arguments.");
		return 1;
	}
	
	pEvent->numOfParams = n;

	for (i = 1; i <= (n); i++) {	
		pEvent->m_param[n-i] = (unsigned int) lua_tonumber(luaVM,-i);
		CConsole::outTime("Argument #%i : %u", -i, pEvent->m_param[n-i]);
	}
	return 1;
}

int l_setEventCallbackFunc( lua_State* luaVM, CEventPacket * pEvent) {
	if(pEvent == NULL) {
		return 0;
	}
	char callbackFunc[255];
	
	//= lua_tostring(luaVM, -1); 
	memset(callbackFunc,0,255);
	sprintf(callbackFunc, lua_tostring(luaVM, -1));
	memset(pEvent->m_eventScriptFunction,0,255);
	
	memcpy(pEvent->m_eventScriptFunction, callbackFunc, strlen(callbackFunc));

	return 0;
}

int l_setEventCallbackFile( lua_State* luaVM, CEventPacket * pEvent) {
	if(pEvent == NULL) {
		return 0;
	}
	const char * callbackFile = lua_tostring(luaVM, -1); 

	memcpy(pEvent->m_eventScript, callbackFile, strlen(callbackFile));

	return 0;
}

int l_startEvent(lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}
	CEventPacket * ptrCep  = Lunar<CEventPacket>::check(luaVM, 1);
	((CChar*)pEntity)->inEvent = true;
	ptrCep->createPacket((CChar*)pEntity);

	return 0;
}

int l_sendSpecialMessage(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if ((n < 1) || (n > 6)) {
		CConsole::outTime("Not enough information to run \nspecialMessage(messageID[,param1,param2,param3,param4,showName])");
		return 0;
	}

	unsigned short messageID;
	unsigned long param1, param2, param3, param4;
	bool showName;

	messageID = (unsigned short)lua_tointeger(luaVM,-(n));

	//Optional field location
	param1 = ((n>=2)?(unsigned long)lua_tointeger(luaVM,-(n-1)):0);
	param2 = ((n>=3)?(unsigned long)lua_tointeger(luaVM,-(n-2)):0);
	param3 = ((n>=4)?(unsigned long)lua_tointeger(luaVM,-(n-3)):0);
	param4 = ((n>=5)?(unsigned long)lua_tointeger(luaVM,-(n-4)):0);
	showName = (lua_toboolean(luaVM,-(n-5)) == 1) ? true : false;

	((CChar*)pEntity)->sendSpecialMessage(messageID, param1, param2, param3, param4, showName);
	
	return 1;
}

int l_addQuest(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \naddQuest(logID,questID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	if(logID >= 10) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);
	if(status > 0) {
		CConsole::outDebOnly("Quest %i already active or completed.", questMissionID);
		return 0;
	}

	// Set quest to current status
	((CChar*)pEntity)->setQuestMissionLogStatus(logID, questMissionID, 0x01);

	return 1;
}

int l_completeQuest(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \ncompleteQuest(logID,questID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	if(logID >= 10) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);
	if(status != 1) {
		CConsole::outDebOnly("Quest %i not active or already completed", questMissionID);
		return 0;
	}

	// Set quest to completed status
	((CChar*)pEntity)->setQuestMissionLogStatus(logID, questMissionID, 0x02);

	return 1;
}

int l_getQuestStatus(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \ngetQuestStatus(logID,questID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	if(logID >= 10) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);

	lua_pushinteger( luaVM, status );

	return 1;
}
int l_learnWs(		lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned short wsId = (unsigned short)lua_tointeger(luaVM,-1);

	((CChar*)pEntity)->learnWeaponSkill(wsId);
	((CChar*)pEntity)->updateAbilities();

	return 1;
}
int l_addSpell(	lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned short spellId = (unsigned short)lua_tointeger(luaVM,-1);

	if (!((CChar*)pEntity)->hasSpell(spellId)) {
		((CChar*)pEntity)->sp.spellList[spellId/8] ^= (int)pow((double)2.0,(int)(spellId % 8));
		((CChar*)pEntity)->updateSpells();
	}

	return 1;
}
int l_hasSpell(	lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned short spellId = (unsigned short)lua_tointeger(luaVM,-1);

	if (((CChar*)pEntity)->hasSpell(spellId)) {
		lua_pushinteger(luaVM,1);
	} else {
		lua_pushinteger(luaVM,0);
	}
	return 1;
}


int l_currentMission(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \nacurrentMission(logID,missionID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	logID += 10;
	if(logID >= 21) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);
	if(status > 0) {
		CConsole::outDebOnly("Mission %i already active or completed.", questMissionID);
		return 0;
	}

	// Set quest to current status
	((CChar*)pEntity)->setQuestMissionLogStatus(logID, questMissionID, 0x01);

	return 1;
}

int l_completeMission(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \ncompleteMission(logID,missionID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	logID += 10;
	if(logID >= 21) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);
	if(status != 1) {
		CConsole::outDebOnly("Mission %i not active or already completed", questMissionID);
		return 0;
	}

	// Set quest to completed status
	((CChar*)pEntity)->setQuestMissionLogStatus(logID, questMissionID, 0x02);

	return 1;
}


int l_getMissionStatus(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \ngetMissionStatus(logID,missionID)");
		return 0;
	}

	unsigned char logID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char questMissionID = (unsigned char)lua_tointeger(luaVM,-1);
	logID += 10;
	if(logID >= 21) {
		CConsole::outDebOnly("logID %i invalid.", logID);
		return 0;
	}

	unsigned char status = 0x00;
	status = ((CChar*)pEntity)->getQuestMissionLogStatus(logID, questMissionID);

	lua_pushinteger( luaVM, status );

	return 1;
}

int l_increaseStorage(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n < 2) {
		CConsole::outTime("Not enough information to run \nincreaseStorage(storageID,amount)");
		return 0;
	}

	unsigned char invStorID = (unsigned char)lua_tointeger(luaVM,-2);
	unsigned char size = (unsigned char)lua_tointeger(luaVM,-1);

	((CChar*)pEntity)->increaseSizeInvStor(invStorID, size);
	((CChar*)pEntity)->updateInvSize();

	return 1;
}

int l_setLevelCap(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned char levelCap = (unsigned char)lua_tointeger(luaVM, -1); 

	((CChar*)pEntity)->setLevelCap(levelCap);

	return 0;
}

int l_getLevelCap(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned char levelCap = 0x00; 
	levelCap = ((CChar*)pEntity)->getLevelCap();

	lua_pushinteger( luaVM, levelCap );

	return 0;
}

int l_setTitle(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned short title = (unsigned short)lua_tointeger(luaVM, -1); 

	((CChar*)pEntity)->setTitle(title);

	return 0;
}

int l_getTitle(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned short title = 0; 
	title = ((CChar*)pEntity)->getCurrentTitle();

	lua_pushinteger( luaVM, title );

	return 1;
}

int l_spawnPet(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	unsigned char petID = (unsigned char)lua_tointeger(luaVM, -1);

	((CChar*)pEntity)->spawnPet(petID);

	return 0;
}

/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
/// GET FUNCTIONS FOR THE CHARS

int l_getCharName( lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushstring( luaVM, pEntity->getName() );
	return 1;
}

int l_getCharId(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getId() );
	return 1;
}

int l_getCharJob(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->m_jobMain );
	return 1;
}

int l_getCharLvl(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->m_lvlMain );
	return 1;
}

int l_getCharSubLvl(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->m_lvlSub);
	return 1;
}

int l_getCharSubJob(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->m_jobSub );
	return 1;
}

int l_getCharRace(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar*)pEntity)->l.race );
	return 1;
}

int l_getCharNation(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar*)pEntity)->st.n.home );
	return 1;
}

int l_getCharRank(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar*)pEntity)->st.n.rank[((CChar*)pEntity)->st.n.home] );
	return 1;
}

int l_getCharRankPoints(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar*)pEntity)->st.n.rankpoints[((CChar*)pEntity)->st.n.home] );
	return 1;
}

int l_getCharHp(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getHp());
	return 1;
}

int l_getAtt(			lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getAttack());
	return 1;
}

int l_getAcc(			lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	CBaseEntity * target = Lunar<CBaseEntity>::check(luaVM, 1);

	lua_pushinteger( luaVM, pEntity->getAccuracy(target));
	return 1;
}

int l_getDef(			lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getDef());
	return 1;
}

int l_getEva(			lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getEva());
	return 1;
}

int l_getCharMp(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->h.mp );
	return 1;
}

// needs fix for mpbtp
int l_getTp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->h.tp );
	return 1;
}


int l_setSpellAnimation(lua_State* luaVM, CSpell * pSpell) {
	if (pSpell == NULL) {
	    return 0;
    }
    pSpell->setAnimation((unsigned short)lua_tointeger(luaVM, -1));
    return 1;
}


int l_setAbilityAnimation(lua_State* luaVM, CAbility * pAbi) {
	if(pAbi == NULL) {
		return 0;
	}
    pAbi->setAnimation((unsigned short)lua_tointeger(luaVM, -1));
    return 1;
}

int l_getMaxHp(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getMaxHp() );
	return 1;
}

int l_getMaxMp(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getMaxMp() );
	return 1;
}

int l_getCharXPos(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushnumber(luaVM, pEntity->getXPos() );
	return 1;
}

int l_getCharYPos(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushnumber( luaVM,  pEntity->getYPos() );
	return 1;
}

int l_getCharZPos(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushnumber( luaVM,  pEntity->getZPos() );
	return 1;
}

int l_getCharRotation(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushnumber(luaVM, pEntity->loc.p.rotation);
	return 1;
}

int l_getCharZone(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getZone() );
	return 1;
}

int l_getCharPrevZone(	lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar *)pEntity)->loc.prevzone );
	return 1;
}

int l_getCharCurrGil(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	lua_pushinteger( luaVM, ((CChar *)pEntity)->getGil() );
	return 1;
}

int l_getStat( lua_State* luaVM, CBaseEntity * pEntity) {
	if(pEntity == NULL) {
		return 0;
	}
	int statID = (int)lua_tointeger(luaVM, -1); 

	switch(statID) {
	case 1:
		lua_pushinteger( luaVM, (pEntity->stats.str + pEntity->stats.pStr));
		break;
	case 2:
		lua_pushinteger( luaVM, (pEntity->stats.dex + pEntity->stats.pDex));
		break;
	case 3:
		lua_pushinteger( luaVM, (pEntity->stats.vit + pEntity->stats.pVit));
		break;
	case 4:
		lua_pushinteger( luaVM, (pEntity->stats.agi + pEntity->stats.pAgi));
		break;
	case 5:
		lua_pushinteger( luaVM, (pEntity->stats.inte + pEntity->stats.pInt));
		break;
	case 6:
		lua_pushinteger( luaVM, (pEntity->stats.mnd + pEntity->stats.pMnd));
		break;
	case 7:
		lua_pushinteger( luaVM, (pEntity->stats.chr + pEntity->stats.pChr));
		break;
	default:
		break;
	}
	return 1;
}

int l_getTarget(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	CBaseEntity * target;
	target = pEntity->m_pTarget;
	if(target == NULL)
		return 0;
	Lunar<CBaseEntity>::push(luaVM,target);
	return 1;
}

int l_getElementalResist(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	float resist = 1;
	unsigned short element = (unsigned short)lua_tointeger(luaVM, -1);
	switch (element) {
		case 0x01: //Fire
			resist = pEntity->r.fire;
			break;
		case 0x02: //Earth
			resist = pEntity->r.earth;
			break;
		case 0x03: //Water
			resist = pEntity->r.water;
			break;
		case 0x04: //Wind
			resist = pEntity->r.wind;
			break;
		case 0x05: //Ice
			resist = pEntity->r.ice;
			break;
		case 0x06: //Thunder
			resist = pEntity->r.thunder;
			break;
		case 0x07: //Light
			resist = pEntity->r.light;
			break;
		case 0x08: //Dark
			resist = pEntity->r.dark;
			break;
		case 0x09: //Impact
			resist = pEntity->r.impact;
			break;
		case 0x0a: //Hand-to-Hand
			resist = pEntity->r.handtohand;
			break;
		case 0x0b: //Pierce
			resist = pEntity->r.pierce;
			break;
		case 0x0c: //Slash
			resist = pEntity->r.slash;
			break;
	}
	lua_pushnumber(luaVM, resist); //is actually returning element ID to the script
	return 1;
}

int l_gainGil(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	unsigned int tmpGil = 0;
	tmpGil = (unsigned int)lua_tointeger(luaVM, -1); 
	((CChar *)pEntity)->addGil(tmpGil);

	return 1;
}

int l_removeGil(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	unsigned int tmpGil = 0;
	tmpGil = (unsigned int)lua_tointeger(luaVM, -1);
	((CChar *)pEntity)->removeGil(tmpGil);
	return 1;
}

int l_getFame(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if(n < 1) {
		CConsole::outErr("getFame Error: 1 parameter expected, %n given!",n);
		return 0;
	}
	unsigned char fameArea = (unsigned char)lua_tointeger(luaVM, -1); 
	unsigned short fame = 0;
	fame = ((CChar*)pEntity)->getFame(fameArea);

	lua_pushinteger( luaVM, fame);

	return 1;
}

int l_getEquip(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if(n < 1) {
		CConsole::outErr("Not enough parameters for getEquip[slotID]!");
		return 0;
	}
	unsigned char equipSlotID = 0;
	equipSlotID = (unsigned char)lua_tointeger(luaVM, -1);
	if( (equipSlotID == 0) || (equipSlotID > 16) ) {
		lua_pushinteger( luaVM, 0);
		return 1;
	}
	unsigned char invSlotID = 0;
	invSlotID = ((CChar*)pEntity)->equip[equipSlotID-1];
	if(invSlotID == 0) {
		lua_pushinteger( luaVM, 0);
		return 1;
	}

	CItem * item = NULL;
	item = ((CChar*)pEntity)->getItem(LOC_INVENTORY, invSlotID);

	if(item == NULL) {
		lua_pushinteger( luaVM, 0);
		return 1;
	}

	unsigned short itemID = 0;
	itemID = item->getId();

	lua_pushinteger( luaVM, itemID);

	return 1;
}

int l_getFameLevel(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if(n < 1) {
		CConsole::outErr("getFameLevel Error: 1 parameter expected, %n given!",n);
		return 0;
	}
	unsigned char fameArea = (unsigned char)lua_tointeger(luaVM, -1); 
	unsigned char fameLevel = 0;
	fameLevel = ((CChar*)pEntity)->getFameLevel(fameArea);

	lua_pushinteger( luaVM, fameLevel);

	return 1;
}


/////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////
/// SET FUNCTIONS FOR THE CHARS

int l_setFame(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if(n < 2) {
		CConsole::outErr("setFame Error: 2 parameter expected, %n given!",n);
		return 0;
	}
	unsigned char fameArea = (unsigned char)lua_tointeger(luaVM, -2); 
	unsigned short fame = (unsigned short)lua_tointeger(luaVM, -1);

	((CChar*)pEntity)->setFame(fameArea, fame);

	return 1;
}

int l_addFame(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if(n < 2) {
		CConsole::outErr("addFame Error: 2 parameter expected, %n given!",n);
		return 0;
	}
	unsigned char fameArea = (unsigned char)lua_tointeger(luaVM, -2);
	unsigned short addFame = (unsigned short)lua_tointeger(luaVM, -1);

	unsigned short fame = ((CChar *)pEntity)->getFame(fameArea);

	((CChar *)pEntity)->setFame(fameArea, fame + addFame);

	return 1;
}

int l_setCharCurrGil(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	unsigned int tmpGil = (unsigned int)lua_tointeger(luaVM, -1);
	((CChar*)pEntity)->setGil(tmpGil);
	return 1;
}

int l_setCharZone(lua_State* luaVM){
	CChar * pChar = (CChar *) lua_tointeger(luaVM, -2); 
	pChar->zoneChar((unsigned char)lua_tointeger(luaVM, -1));
	pChar->zoning = 1;
	return 1;
}

int l_setCharPos(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	if (n > 2) {
		
		((CChar *)pEntity)->loc.p.x = (float) lua_tonumber(luaVM, -(n));
		((CChar *)pEntity)->loc.p.y = (float) lua_tonumber(luaVM, -(n-1));
		((CChar *)pEntity)->loc.p.z = (float) lua_tonumber(luaVM, -(n-2));
		((CChar *)pEntity)->loc.p.rotation = (unsigned short) lua_tointeger(luaVM, -(n-3));
		//Optional fields, rotation and zone
		if (n == 5) {
			((CChar *)pEntity)->zoneChar((unsigned char)lua_tointeger(luaVM, -(n-4)),((CChar *)pEntity)->loc.p);
			((CChar *)pEntity)->zoning = 1;
		} else {
			((CChar *)pEntity)->quePacket(CPositionPacket((CBaseEntity*)((CChar *)pEntity),((CChar *)pEntity)->loc.p));
		}
	} else {
		CConsole::outTime("Not enough information to run \nsetPos(x,y,z[,rotation,zone])");
	}
	return 1;
}


int l_setCharAnimation(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar *)pEntity)->setAnimation((unsigned char)lua_tointeger(luaVM, -1)); 

	((CChar *)pEntity)->updateChar();

	return 1;
}

int l_charRankUp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	((CChar *)pEntity)->st.n.rank[((CChar *)pEntity)->st.n.home] += 1;
	((CChar *)pEntity)->st.n.rankpoints[((CChar *)pEntity)->st.n.home] = 0; 

	return 1;
}

int l_addCharRankPoints(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	int n = lua_gettop(luaVM);
	if (n > 1) {
		CConsole::outTime("Not enough information to run \naddRankPoints(points)");
		return 0;
	}

	((CChar *)pEntity)->st.n.rankpoints[((CChar *)pEntity)->st.n.home] += (unsigned int)lua_tointeger(luaVM,-1); 

	return 1;
}

int l_setHp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}

	if(pEntity->isPlayer())
		if( lua_tointeger(luaVM, -1) >= 0) {
			pEntity->h.hp = (short)lua_tointeger(luaVM, -1);
				((CChar*)pEntity)->updateHealth();
		} 
	else
		if( lua_tointeger(luaVM, -1) >= 0) {
			((CNpc*)pEntity)->h.hp = (short)lua_tointeger(luaVM, -1);
				
		} 
	return 1;
}

int l_setTp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	unsigned short TP = 0;
	TP = (unsigned short)lua_tointeger(luaVM, -1);
	if(TP > 300)
		TP = 300;
	pEntity->h.tp = TP;
	if(pEntity->isPlayer())
		((CChar*)pEntity)->updateHealth();

	return 1;
}

int l_setMp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	if(pEntity->getMaxMp() > (unsigned)lua_tointeger(luaVM, -1)) {
		pEntity->h.mp = (short)lua_tointeger(luaVM, -1);
		if(pEntity->isPlayer())
			((CChar*)pEntity)->updateHealth();
	} else {
		pEntity->h.mp = pEntity->getMaxMp();
	}
	return 1;
}

int l_addHp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	unsigned short sum = (unsigned short)pEntity->getHp() + (unsigned short)lua_tointeger(luaVM, -1);

	if(pEntity->isPlayer()) {
		if((pEntity->getMaxHp()) > sum) {
			pEntity->h.hp += (short)lua_tointeger(luaVM, -1);
		} else {
			pEntity->h.hp = (pEntity->getMaxHp());
		}
		((CChar*)pEntity)->updateHealth();
	} else {
		if(pEntity->getMaxHp() > sum) {
			((CNpc*)pEntity)->h.hp += (short)lua_tointeger(luaVM, -1);
		} else {
			((CNpc*)pEntity)->h.hp = pEntity->getMaxHp();
		}
	}
	return 1;
}

int l_addMp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	unsigned short sum = (unsigned short)pEntity->getMp() + (unsigned short)lua_tointeger(luaVM, -1);

	if(pEntity->isPlayer()) {
		if((pEntity->getMaxMp() + ((CChar*)pEntity)->pMP) > sum) {
			pEntity->h.mp += (short)lua_tointeger(luaVM, -1);
		} else {
			pEntity->h.mp = (pEntity->getMaxMp() + ((CChar*)pEntity)->pMP);
		}
		((CChar*)pEntity)->updateHealth();
	} else {
		if(pEntity->getMaxMp() > sum) {
			pEntity->h.mp += (short)lua_tointeger(luaVM, -1);
		} else {
			pEntity->h.mp = pEntity->getMaxMp();
		}
	}
	return 1;
}

int l_removeHp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}

	if(pEntity->isPlayer())
		if(0 < (pEntity->getHp() - lua_tointeger(luaVM, -1))) {
			pEntity->h.hp -= (short)lua_tointeger(luaVM, -1);
				((CChar*)pEntity)->updateHealth();
		} else {
			pEntity->h.hp = 0;
		}
	else
		if(0 < (((CNpc*)pEntity)->getHp() - lua_tointeger(luaVM, -1))) {
			((CNpc*)pEntity)->h.hp -= (short)lua_tointeger(luaVM, -1);
				
		} else {
			((CNpc*)pEntity)->h.hp = 0;
		}
	return 1;
}

int l_removeMp(lua_State* luaVM, CBaseEntity * pEntity ) {
	if(pEntity == NULL) {
		return 0;
	}
	if(0 < (pEntity->getMp() - lua_tointeger(luaVM, -1))) {
		pEntity->h.mp -= (short)lua_tointeger(luaVM, -1);
		if(pEntity->isPlayer())
			((CChar*)pEntity)->updateHealth();
	} else {
		pEntity->h.mp = 0;
	}
	return 1;
}


int l_setCharNation(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	
	((CChar *)pEntity)->st.n.home = (unsigned char) lua_tointeger(luaVM, -1); 

	return 1;
}

int l_getWeaponDelay(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	lua_pushinteger( luaVM, ((CChar*)pEntity)->getDelay(SLOT_MAIN));

	return 1;
}




int l_setCharFameSan(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar *)pEntity)->fameSan = (unsigned short) lua_tointeger(luaVM, -1); 

	return 1;
}

int l_setCharFameBas(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar *)pEntity)->fameBas = (unsigned short) lua_tointeger(luaVM, -1); 

	return 1;
}

int l_setCharFameWin(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar *)pEntity)->fameWin = (unsigned short) lua_tointeger(luaVM, -1); 

	return 1;
}

int l_setCharFameNorg(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar *)pEntity)->fameNorg = (unsigned short) lua_tointeger(luaVM, -1); 

	return 1;
}

int l_CharAddItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()){
		return 0;
	}
	int n = lua_gettop(luaVM);

	unsigned short itemID;
	itemID = (unsigned short)lua_tointeger(luaVM, -n);
	if(itemID == 0)
		return 0;

	unsigned long quantity = ((n>=2)?(unsigned long)lua_tointeger(luaVM,-(n-1)):1);

	((CChar*)pEntity)->addItem(itemID,0,quantity);
	((CChar*)pEntity)->sendFinishInvStor();
	return 1;
}

// sets a variable for a char, creates it if not existant
int l_setCharVar(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	const char * variable =  lua_tostring(luaVM, -2); 
	int value       =  (int)lua_tointeger(luaVM, -1); 
	
	((CChar*)pEntity)->setCharVar(variable,value);

	return 0;
}

// retrieves a variable from a user, returns 0 if not existant
int l_getCharVar(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	const char * variable =  lua_tostring(luaVM, -1); 
	int value = ((CChar*)pEntity)->getCharVar(variable);
	lua_pushinteger( luaVM, value);
	return 1;
	
}

int l_CharGetItemInfo(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	unsigned char slotID, locationID;
	int n = lua_gettop(luaVM);
	if (n < 1) {
		CConsole::outTime("Not enough information to run \ngetItemInfo(slotID[,location])");
		return 0;
	}
	slotID = (unsigned char)lua_tointeger(luaVM,-(n));
	//Optional field location
	locationID = ((n==2)?(unsigned char)lua_tointeger(luaVM,-(n-1)):0);
	CItem * item;
	item = ((CChar*)pEntity)->getItem(locationID, slotID);
	if(item == NULL)
		return 0;

	unsigned short itemID = item->getId();
	unsigned long quantity = item->getQuantity();

	lua_pushinteger( luaVM, itemID);
	lua_pushinteger( luaVM, quantity);
	return 2;
}

int l_CharHasItem(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}
	int n = lua_gettop(luaVM);
	if ((n < 1) || (n > 2)) {
		CConsole::outTime("Not enough information to run \nhasItem(ItemID[,location])");
		return 0;
	}

	unsigned short itemID;
	unsigned long quantity;
	unsigned char locationID;

	itemID = (unsigned short)lua_tointeger(luaVM,-(n));

	//Optional field location
	locationID = ((n==2)?(unsigned short)lua_tointeger(luaVM,-(n-1)):0);
	
	quantity = ((CChar*)pEntity)->getItemQuantity(locationID, itemID);

	lua_pushinteger( luaVM, quantity);
	return 1;
}

int l_CharTakeItem(lua_State* luaVM, CBaseEntity * pEntity) {
	if((pEntity == NULL) || !pEntity->isPlayer()){
		CConsole::outErr("pEntity is not a player object!");
		return 0;
	}

	int n = lua_gettop(luaVM);
	unsigned short itemID;
	unsigned char locationID;
	unsigned long quantity;

	if (n < 1) {
		CConsole::outTime("Not enough information to run \nremoveItem(itemID[,quantity,location])");
		return 0;
	}
	
	itemID = (int) lua_tointeger(luaVM,-(n));

	//Optional fields quantity and location
	quantity = ((n>=2)?(unsigned long)lua_tointeger(luaVM,-(n-1)):1);
	locationID = ((n==3)?(unsigned char)lua_tointeger(luaVM,-(n-2)):0);

	if(n == 2) {
		unsigned long totalQty = ((CChar*)pEntity)->getItemQuantity(locationID, itemID);
		if(quantity > totalQty)
			return 0;
	}
	((CChar*)pEntity)->removeItemQuantity(itemID, locationID, quantity);
	return 1;
}


int l_CharAddAndEquipItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int n = lua_gettop(luaVM);
	unsigned short itemID;
	unsigned char invSlotID;

	if (n < 1) {
		CConsole::outTime("Not enough information to run \naddAndEquip(itemID)");
		return 0;
	}
	itemID = (int) lua_tointeger(luaVM, -1);
	invSlotID = ((CChar*)pEntity)->addItem(itemID, LOC_INVENTORY,1);
	if(invSlotID == 0xFF) {
		return 0;
	}
	CItem * item;
	item = ((CChar*)pEntity)->getItem(LOC_INVENTORY, invSlotID);
	if(item == NULL) {
		CConsole::outDebOnly("Error adding item.  Cannot equip.");
		return 0;
	}
	unsigned char equipSlotID;
	if(item->getType() == ITEM_ARMOR)
		equipSlotID = ((CArmorItem*)item)->getEquipSlotId();
	else if(item->getType() == ITEM_WEAPON)
		equipSlotID = ((CWeaponItem*)item)->getEquipSlotId();
	else {
		CConsole::outDebOnly("New item is not equipable.");
		return 0;
	}
	((CChar*)pEntity)->equipItem(invSlotID, equipSlotID);
	return 1;
}


int l_addKeyItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int keyItemId = (int) lua_tonumber(luaVM, -1);

	if(((CChar*)pEntity)->hasKeyItem(keyItemId)) {
		return 0;
	}

	((CChar*)pEntity)->k.keyList[keyItemId/8] ^= (int)pow((double)2.0,(int)(keyItemId % 8));
	((CChar*)pEntity)->updateKeyItems();
	
	return 1;
}

int l_hasKeyItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int keyItemId =  (int) lua_tonumber(luaVM, -1);

	if(((CChar*)pEntity)->hasKeyItem(keyItemId)) {
		lua_pushinteger( luaVM, 1 );
		return 1;
	}

	lua_pushinteger( luaVM, 0 );
	return 1;
}

int l_seenKeyItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int keyItemId = (int) lua_tonumber(luaVM, -1);

	if(((CChar*)pEntity)->seenKeyItem(keyItemId)) {
		lua_pushinteger( luaVM, 1 );
		return 1;
	}

	lua_pushinteger( luaVM, 0 );
	return 1;
}

int l_removeKeyItem(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int keyItemId = (int) lua_tonumber(luaVM, -1);

	if(((CChar*)pEntity)->hasKeyItem(keyItemId)) {
		((CChar*)pEntity)->k.keyList[keyItemId/8] ^= (int)pow((double)2,(int)(keyItemId % 8));
		((CChar*)pEntity)->k.seenList[keyItemId/8] ^= (int)pow((double)2,(int)(keyItemId % 8));
	}
	((CChar*)pEntity)->updateKeyItems();	
	return 1;
}

int l_sendRaise(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	int raiseLevel = (int) lua_tonumber(luaVM, -1);
	if(((CChar*)pEntity)->m_hasRaise == 0) {
		((CChar*)pEntity)->m_hasRaise = raiseLevel;
		((CChar*)pEntity)->quePacket(CRaiseTractorMenuPacket((CChar*)pEntity, 1));	
	}
	return 1;
}

int l_sendTractor(lua_State* luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}

	if(((CChar*)pEntity)->m_hasTractor == false) {
		((CChar*)pEntity)->m_hasTractor = true;

		((CChar*)pEntity)->m_tractorPos.x = (float) lua_tonumber(luaVM, -1);
		((CChar*)pEntity)->m_tractorPos.y = (float) lua_tonumber(luaVM, -2);
		((CChar*)pEntity)->m_tractorPos.z = (float) lua_tonumber(luaVM, -3);
		((CChar*)pEntity)->m_tractorPos.rotation = (int) lua_tonumber(luaVM, -4);
		
		((CChar*)pEntity)->quePacket(CRaiseTractorMenuPacket((CChar*)pEntity, 2));	
	}
	return 1;
}



int l_isPlayer(lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	if(pEntity->isPlayer()) {
		lua_pushinteger( luaVM, 1);
	} else {
		lua_pushinteger( luaVM, 0);
	}
	return 1;
}

int l_isNPC(lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	if(pEntity->isNPC()) {
		lua_pushinteger( luaVM, 1);
	} else {
		lua_pushinteger( luaVM, 0);
	}
	return 1;
}

int l_isMob(lua_State* luaVM, CBaseEntity * pEntity){
	if(pEntity == NULL) {
		return 0;
	}
	if(pEntity->isMob()) {
		lua_pushinteger( luaVM, 1);
	} else {
		lua_pushinteger( luaVM, 0);
	}
	return 1;
}

int l_getBaseDamage(	lua_State* luaVM, CBaseEntity * pEntity) {

	if(pEntity == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pEntity->getWeaponDamage());

	return 1;

}

int l_getVanaYear(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getYear());
	return 1;
}
int l_getVanaMonth(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getMonth());
	return 1;
}
int l_getVanaDayElement(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getWeekday());
	return 1;
}
int l_getVanaDay(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getDayOfTheMonth());
	return 1;
}
int l_getVanaHour(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getHour());
	return 1;
}

int l_getVanaMinute(lua_State* luaVM){
	lua_pushinteger( luaVM, CVanaTime::getInstance()->getMinute());
	return 1;
}

int l_getMPCost(lua_State* luaVM, CSpell* spell){
	if(spell == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, spell->getMPCost());
	return 1;
}

int l_getSpellElement(lua_State* luaVM, CSpell* spell){
if(spell == NULL) {
		return 0;
	}

	lua_pushinteger( luaVM, spell->getElement());
	return 1;
}


int l_setMsg(lua_State* luaVM, CSpell* spell){
	if(spell == NULL) {
		return 0;
	}

	spell->setSpellDefaultMsgType((unsigned int)lua_tonumber(luaVM, -1));
	return 1;
}

int l_getCurrentSpellTargetNum(lua_State* luaVM, CSpell* spell){
	if(spell == NULL) {
		return 0;
	}

	lua_pushinteger( luaVM, spell->getCurrentTargetNum());
	return 1;
}

int l_getSpellTargetNum(lua_State* luaVM, CSpell* spell){
	if(spell == NULL) {
		return 0;
	}

	lua_pushinteger( luaVM, spell->m_targetList.size());
	return 1;
}

int l_getTradeGil(	lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		lua_pushinteger( luaVM, 0);
		return 0;
	}
	lua_pushinteger( luaVM, pItems->getGil());
	return 1;
}

int l_getItemID(	lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	unsigned char slotID = (unsigned char) lua_tonumber(luaVM, -1);
	lua_pushinteger( luaVM, pItems->getItemID(slotID));
	return 1;
}

int l_getItemQuantity(	lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	unsigned short itemID = (unsigned short) lua_tonumber(luaVM, -1);
	lua_pushinteger( luaVM, pItems->getItemQuantity(itemID));
	return 1;
}

int l_getSlotQuantity(	lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	unsigned char slotID = (unsigned char) lua_tonumber(luaVM, -1);
	lua_pushinteger( luaVM, pItems->getQuantity(slotID));
	return 1;
}

int l_getItemCount(	lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	lua_pushinteger( luaVM, pItems->getTotalQuantity());
	return 1;
}

int l_hasItemQuantity( lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	unsigned int quantity = (unsigned int) lua_tonumber(luaVM, -1);
	unsigned short itemID = (unsigned short) lua_tonumber(luaVM, -2);
	unsigned int tradeQuantity = pItems->getItemQuantity(itemID);
	if(quantity != tradeQuantity)
		lua_pushboolean( luaVM, false);
	else
		lua_pushboolean( luaVM, true);
	return 1;
}


int l_setAbilityMessage(lua_State* luaVM, CAbility * pAbi) {
	if(pAbi == NULL) {
		return 0;
	}

	pAbi->setMessage((unsigned short)lua_tointeger(luaVM, -1));
	return 1;
}

int l_setAbilityResult(lua_State* luaVM, CAbility * pAbi) {
	if(pAbi == NULL) {
		return 0;
	}

	pAbi->setResult((unsigned short)lua_tointeger(luaVM, -1));
	return 1;
}

int l_setItemMessage(lua_State* luaVM, CItem * pItem) {
	if (pItem == NULL) {
		return 0;
	}

	pItem->setMessage((unsigned short)lua_tointeger(luaVM, -2));
	pItem->m_paramtwo = (short)lua_tointeger(luaVM, -1);
	return 1;
}

int l_setItemAnimation(lua_State* luaVM, CItem * pItem) {
	if (pItem == NULL) {
		return 0;
	}

	pItem->setAnimation((unsigned short)lua_tointeger(luaVM, -1));
	return 1;
}

int l_setItemRemove(lua_State* luaVM, CItem * pItem) {
	if (pItem == NULL) {
		return 0;
	}
	pItem->m_canRemove = false;
	return 1;
}

int l_tradeComplete( lua_State* luaVM, CTradeContainer * pItems ) {
	if(pItems == NULL) {
		return 0;
	}
	CChar * schar;
	schar = pItems->getOwner();
	if(schar == NULL) {
		return 0;
	}
	unsigned long gil = pItems->getGil();
	schar->removeGil(gil);
	for(unsigned char slotID = 1; slotID <= 8; slotID++) {
		if(pItems->getInvSlotID(slotID) > 0) 
		{
			unsigned char invSlotID = pItems->getInvSlotID(slotID);
			unsigned long quantity = pItems->getQuantity(slotID);
			schar->inventory->removeQuantity(invSlotID, quantity);
			schar->updateInvStor(LOC_INVENTORY, invSlotID);
			schar->sendUpdateInvStor(invSlotID, LOC_INVENTORY, schar->inventory->getQuantity(invSlotID));
		}
	}
	schar->sendFinishInvStor();
	return 1;
}

int l_registerRegion(lua_State* luaVM){
	int n = lua_gettop(luaVM);
	if (n < 8) {
		CConsole::outTime("Not enough information to run \nregisterRegion(zone,regionNumber,x1,x2,y1,y2,z1,z2)");
		return 0;
	}
	unsigned short zone = (unsigned short) lua_tointeger(luaVM, -8);

	if(zone >= 0 && zone <= 255){
		struct regions defineRegion;
		defineRegion.ID = (int) lua_tonumber(luaVM, -7);
		defineRegion.x1 = (float) lua_tonumber(luaVM, -6);
		defineRegion.x2 = (float) lua_tonumber(luaVM, -5);
		defineRegion.y1 = (float) lua_tonumber(luaVM, -4);
		defineRegion.y2 = (float) lua_tonumber(luaVM, -3);
		defineRegion.z1 = (float) lua_tonumber(luaVM, -2);
		defineRegion.z2 = (float) lua_tonumber(luaVM, -1);
		CLuaManager::regionList[zone].push_back(defineRegion);
	}
	return 1;
}

int l_sendTellMsg(lua_State *L){
	CPacket::sendServerTell((char *)lua_tostring(L,-3),(char *)lua_tostring(L,-2),(char *)lua_tostring(L,-1));
	return 1;
}

int l_sendMenu(lua_State *luaVM, CBaseEntity * pEntity){
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	int menu = (int)lua_tointeger(luaVM, -1);
	switch(menu) {
		case 1:
			((CChar*)pEntity)->sendMogMenu();
			break;
		default:
			CConsole::outTime("Menu %i not implemented, yet.",menu);
			break;
	}
	return 1;
}

int l_getZone(lua_State* luaVM) {
	unsigned char zoneID = (unsigned char)lua_tointeger(luaVM, -1); 
	CZone * pZone = NULL;
	pZone = CZoneHandler::getInstance()->getZone(zoneID);
	if(pZone == NULL)
		return 0;

	Lunar<CZone>::push(luaVM,pZone);
	return 1;
}

int l_getWeather(lua_State* luaVM, CZone * pZone) {
	if(pZone == NULL) {
		return 0;
	}

	weather wthr;
	memset(&wthr, 0, sizeof(weather));
	wthr = pZone->getCurrentWeather();
	lua_pushinteger( luaVM, wthr.weatherEffect );

	return 1;
}

int l_executeSQL(lua_State *L)
{
	if(lua_isstring(L, 1))
	{
		MYSQL* mysqlConn=CDb::getConn(); //get conn
		if(mysql_query(mysqlConn, lua_tostring(L, 1)))
		{
			CConsole::outTime("LUA SQL FAILED: %s ",lua_tostring(L,1));
			CDb::pushConn(mysqlConn);
		}
		else 
		{
			MYSQL_RES *result = mysql_store_result(mysqlConn);
			CDb::pushConn(mysqlConn); //release conn

			MYSQL_ROW row;
			lua_newtable(L);
			int rowcount = 1;
			if (result){
				while(row = mysql_fetch_row(result)){
					lua_newtable(L);
					lua_pushnumber(L, rowcount);
					lua_pushvalue(L, -2);
					lua_settable(L, -4);
					unsigned int fields = mysql_num_fields(result);
					for(unsigned int i = 0; i < fields; i++){
						lua_pushnumber(L, i + 1);
						lua_pushstring(L, row[i]);
						lua_settable(L, -3);
					}
					lua_pop(L, 1);
					rowcount++;
				}
			}
		}
	}
	return 1;
}

int l_spawnMob(lua_State *L, CZone * pZone ) {
	if(pZone == NULL) {
		return 0;
	}
	struct position pos;
	const char * name	=  lua_tostring(L, -6); 
	pos.x				=  (float)lua_tonumber(L, -5); 
	pos.y				=  (float)lua_tonumber(L, -4); 
	pos.z				=  (float)lua_tonumber(L, -3); 
	pos.rotation		=  (unsigned short)lua_tonumber(L, -2); 
	unsigned int owner	=  (unsigned int)lua_tonumber(L, -1); 
	
	CMobHandler::getInstance()->spawnMobScripted((char*)name, pos, (unsigned char)pZone->getId(), owner);

	return 1;
}


int	l_setGlobalVar(lua_State *L) {
	const char * variable =  lua_tostring(L, -2); 
	int value			  =  (int)lua_tointeger(L, -1); 
	MYSQL_RES * res;
	char query[4384];
	int count = 0;


	memset(query,0,4384);
	sprintf(query, "SELECT value FROM server_vars WHERE ident = '%s' ", variable);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
	{
		mysql_free_result(res);
		return 0;
	}
	if(!mysql_num_rows(res)){
		memset(query,0,4384);
		sprintf(query, "INSERT INTO server_vars (ident, value) VALUES ('%s',%i) ", variable, value);
		count = (int) strlen(query);

		mysqlConn=CDb::getConn(); //get conn
		mysql_real_query(mysqlConn, query, count);
		CDb::pushConn(mysqlConn); //release conn

		mysql_free_result(res);
		return 1;
	}
	
	memset(query,0,4384);
	sprintf(query, "UPDATE server_vars SET value = %i WHERE ident = '%s' ", value, variable);
	count = (int) strlen(query);

	mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);
	CDb::pushConn(mysqlConn); //release conn

	mysql_free_result(res);
	
	return 1;
}


// retrieves a variable from a the server var table, returns 0 if not existant
int	l_getGlobalVar(lua_State* luaVM) {
	const char * variable =  lua_tostring(luaVM, -1); 
	
	MYSQL_RES * res;
	MYSQL_ROW row;
	char query[4384];
	int count = 0;
    int value;

	memset(query,0,4384);
	sprintf(query, "SELECT value FROM server_vars WHERE ident = '%s' ", variable);
	count = (int) strlen(query);

	MYSQL* mysqlConn=CDb::getConn(); //get conn
	mysql_real_query(mysqlConn, query, count);

	res = mysql_store_result(mysqlConn);
	CDb::pushConn(mysqlConn); //release conn

	if(!res)
	{
		mysql_free_result(res);
		value = 0;
		lua_pushinteger( luaVM, value);
		return 1;
	} 

	if(!mysql_num_rows(res)){
		mysql_free_result(res);
		value = 0;
		lua_pushinteger( luaVM, value);
		return 1;
	}

	row = mysql_fetch_row(res); 
	value = atoi(row[0]);
	mysql_free_result(res);

	lua_pushinteger( luaVM, value);
	return 1;
	
}

int l_setCharHomePoint(lua_State* luaVM, CBaseEntity * pEntity ) {
	if((pEntity == NULL) || !pEntity->isPlayer()) {
		return 0;
	}
	((CChar*)pEntity)->st.homepoint = pEntity->loc.zone;

	//Actually set the homepoint.
	if (pEntity->loc.p.x != NULL) {
		((CChar*)pEntity)->m_homePoint.x = pEntity->loc.p.x;
		((CChar*)pEntity)->m_homePoint.y = pEntity->loc.p.y;
		((CChar*)pEntity)->m_homePoint.z = pEntity->loc.p.z;
		((CChar*)pEntity)->m_homePoint.rotation = pEntity->loc.p.rotation;
	} else {
		((CChar*)pEntity)->m_homePoint.x = 0;
		((CChar*)pEntity)->m_homePoint.y = 0;
		((CChar*)pEntity)->m_homePoint.z = 0;
		((CChar*)pEntity)->m_homePoint.rotation = 0;
	}

	((CChar*)pEntity)->updateChar();
	((CChar*)pEntity)->writeChar();
	return 1;
}